MongoDB 数据建模与实战模式
MongoDB 系统讲解第三篇:怎么设计文档结构。03-理解篇是我自己的思维对照(反范式化、聚合根),这一篇把书里的建模方法论补全——内嵌还是引用、常见设计模式、大文档治理。
内嵌 vs 引用:决策清单
这是 MongoDB 建模的第一决策,书里的判断条件汇总:
| 判断维度 | 倾向内嵌 | 倾向引用 |
|---|---|---|
| 数据被谁拥有 | 一对一 / 少量的一对多(如订单的收货地址) | 多对多 / 被多个文档共享 |
| 是否独立访问 | 子数据总是跟着父文档一起取 | 子数据被单独查询/更新 |
| 增长是否可控 | 数量有上界(评论数封顶几百) | 无限增长(时间线、日志) |
| 更新模式 | 一起更新 | 各自独立高频更新 |
文档上限 16MB、嵌套层级上限 100 层是硬约束;数组无限膨胀是内嵌最常见的翻车点。
一对多的三种实现(书里的经典划分):
- 一对少量 → 内嵌数组
- 一对多且"多"端独立查询 → 子文档存父 id(引用)
- 一对超级多(如留言板) → "多"端存父 id(父写子,反过来父存数组必爆)
实战设计模式(精选四个)
桶模式——时间序列数据(传感器/监控)别一条数据一个文档(文档数爆炸),按小时/天"装桶":
// 一天一个文档,measurements 数组装当天的点
{ "_id": "sensor1-20260831", "date": "2026-08-31",
"readings": [ {"t": 0, "temp": 22.1}, {"_id": 1, "temp": 22.3}, ... ],
"count": 1440 }
计算模式——频繁读的统计值不实时算,写入时顺带维护(或定时重算):帖子的 commentCount 就存在帖子里,别每次 count。
扩展引用——反范式化但克制:订单里内嵌"商品名 + 价格快照"而不是整个商品文档,避免读时多一次 JOIN;快照还能保住"下单时的价格"语义。
属性模式——文档里一堆同类属性(颜色/尺寸/材质…)塞进一个数组 [{k, v}],方便统一加索引查询,应对"字段经常变"的诉求。
💡 这些模式的名字不用背,场景对了自然想起:序列化数据想省文档数→桶;读密集统计→计算;免 JOIN 但又怕重复全量→扩展引用。
大文档与增长治理
- 单文档 16MB 上限:图片/长文本别硬塞,存对象存储引用 URL
- 数组要设上界:评论数可能无限涨的,别内嵌——引用化或分页子集合
- 大数组的 $push 会越来越慢(文档挪动/索引维护),观察
docSizes指标 - 需要"无限增长 + 独立查询"的数据,那就是一个独立集合,别硬塞进父文档
从关系型迁移的思维转换
| 关系型直觉 | MongoDB 做法 |
|---|---|
| 先拆表规范化,查询再 JOIN | 先想查询长什么样,按查询建模(读优先) |
| JOIN 日常操作 | $lookup 能用但贵,能用内嵌就内嵌 |
| 第三范式洁癖 | 反范式化是常规武器(付冗余、买读取效率) |
| 事务靠数据库默认保证 | 单文档原子天然有,跨文档才开事务(4.0+) |
| 外键约束 | 没有外键——引用一致性靠应用层维护 |
💡 03-理解篇的核心论点在这里得到印证:MongoDB 是"面向读取建模"的数据库——建模前先写好你最频繁的那几条查询,再决定文档形状。反过来照搬关系型的表结构,是新手最大的坑(书里原话大意:从关系型带来的第一直觉,往往是这里最需要怀疑的东西)。
⬅️ 05-MongoDB 副本集与分片 🏠 00-数据库 ➡️ 00-向量数据库概览
💬 评论